<?php

namespace app\service;

use FFMpeg\Coordinate\Dimension;
use FFMpeg\Coordinate\FrameRate;
use FFMpeg\Coordinate\Point;
use FFMpeg\Coordinate\TimeCode;
use FFMpeg\FFMpeg;
use FFMpeg\FFProbe;
use FFMpeg\Format\Audio\Aac;
use FFMpeg\Format\Audio\Flac;
use FFMpeg\Format\Audio\Mp3;
use FFMpeg\Format\Audio\Vorbis;
use FFMpeg\Format\Audio\Wav;
use FFMpeg\Format\Video\X264;
use support\Request;
use support\Response;

class FfmpegService
{

    protected $ffmpeg;
    protected $ffprobe;
    protected $format;
    public function __construct()
    {
        //创建FFMpeg对象
        $this->ffmpeg = FFMpeg::create([
            'ffmpeg.binaries' => '/www/server/ffmpeg/ffmpeg-6.1/ffmpeg',
            'ffprobe.binaries' => '/www/server/ffmpeg/ffmpeg-6.1/ffprobe',
            'timeout'=>3600,
            'ffmpeg.threads'=>12,
        ]);
        $this->ffprobe=FFProbe::create([
            'ffmpeg.binaries' => '/www/server/ffmpeg/ffmpeg-6.1/ffmpeg',
            'ffprobe.binaries' => '/www/server/ffmpeg/ffmpeg-6.1/ffprobe',
            'timeout'=>3600,
            'ffmpeg.threads'=>12,
        ]);
        $this->format = new X264('aac', 'libx264');
    }

    /**
     * 单码率m3u8
     * @param string $video_path mp4源文件路径
     * @param string $m3u8_path m3u8文件路径
     * @return void
     */
    public function getM3u8(string $video_path="/video/20240123/113799054.mp4",string $m3u8_path="/video/20240123/113799054.m3u8")
    {
        $format = $this->format;
        //setKiloBitrate 设置视频比特率 setAudioChannels 声道设置，1单声道，2双声道，3立体声 setAudioKiloBitrate 设置音频比特率
        //$format->setKiloBitrate(1000)->setAudioChannels(2)->setAudioKiloBitrate(256);
        $paths = public_path($video_path);
        $mainPlaylistPath = public_path($m3u8_path);
        // 创建FFMpeg对象
        $ffmpeg = $this->ffmpeg;
        // 输入视频文件路径
        $video = $ffmpeg->open($paths);
        // 转码并设置输出路径和格式
        $video->save($format, $mainPlaylistPath);
    }

    /**
     * 多码率m3u8
     * @param string $video_path mp4源文件路径
     * @param string $m3u8_path m3u8文件路径
     * @return void
     */
    public function getMultiRateM3u8(string $video_path="/video/20240123/113799054.mp4",string $m3u8_path="/video/20240123/113799054.m3u8")
    {
        $format = $this->format->setKiloBitrate(250)->setKiloBitrate(500)->setKiloBitrate(1000);
        //setKiloBitrate 设置视频比特率 setAudioChannels 声道设置，1单声道，2双声道，3立体声 setAudioKiloBitrate 设置音频比特率
        //$format->setKiloBitrate(1000)->setAudioChannels(2)->setAudioKiloBitrate(256);
        $paths = public_path($video_path);
        $mainPlaylistPath = public_path($m3u8_path);
        // 创建FFMpeg对象
        $ffmpeg = $this->ffmpeg;
        // 输入视频文件路径
        $video = $ffmpeg->open($paths);
        // 转码并设置输出路径和格式
        $video->save($format, $mainPlaylistPath);
    }


    /**
     * MP4视频中提取一张图片
     * @param string $video_path
     * @param string $image_path
     * @param int $quantity
     * @return string|Response
     */
    public function getMp4Image(string $video_path,string $image_path,int $quantity=20)
    {
        if (strpos($video_path,'.mp4') === false) {
            return errorMessage("视频格式不正确");
        }
        if (strpos($image_path,'.jpg') === false || strpos($image_path,'.png') === false || strpos($image_path,'.jpeg') === false) {
            return errorMessage("图片格式不正确");
        }
        /** 1. 初始化配置*/
        $ffmpeg = $this->ffmpeg;
        $paths = public_path($video_path);
        $mainPlaylistPath = public_path($image_path);
        /** 2. 读取视频文件*/
        $video = $ffmpeg->open($paths);

        /** 3. 提取一张图片，从视频的 20 秒处 */
        $video->frame(TimeCode::fromSeconds($quantity))
            ->save($mainPlaylistPath);
        return $mainPlaylistPath;
    }

    /**
     * 给MP4视频添加水印
     * @param string $video_path 原视频路径
     * @param string $image_path 水印图片路径
     * @param string $new_video_path 输出视频路径
     * @param int $bottom
     * @param int $right
     * @return string|Response
     */
    public function getWatermark(string $video_path,string $image_path,string $new_video_path,int $bottom,int $right)
    {
        if (strpos($video_path,'.mp4') === false) {
            return errorMessage("视频格式不正确");
        }
        if (strpos($new_video_path,'.mp4') === false) {
            return errorMessage("视频格式不正确");
        }
        if (strpos($image_path,'.jpg') === false || strpos($image_path,'.png') === false || strpos($image_path,'.jpeg') === false) {
            return errorMessage("图片格式不正确");
        }
        /** 1. 初始化配置*/
        $ffmpeg = $this->ffmpeg;
        $paths = public_path($video_path);
        /** 2. 读取视频文件*/
        $video = $ffmpeg->open($paths);
        /** 3. 水印图片地址 */
        $watermarkpath = public_path($image_path);
        /** 4. 视频水印处理 */
        $video->filters()->watermark($watermarkpath, [
            'position' => 'relative',
            'bottom' =>$bottom,
            'right' =>$right,
        ]);
        /** 5. 解码和编码格式。x264是用于编码H.264/MPEG-4 AVC视频流的免费软件库 */
        $format = $this->format;
        /** 6. 保存添加水印视频文件 */
        $mainPlaylistPath = public_path($new_video_path);
        $video->save($format, $mainPlaylistPath);
        return $mainPlaylistPath;
    }

    /**
     * 多个视频文件合并成一个mp4文件
     * @param array $inputFiles 按照顺序合并的文件
     * @param string $outputPathfile 输出文件路径
     * @return void
     */
    public function getMergeTSFiles(array $inputFiles,string $outputPathfile)
    {
        $ffmpeg = $this->ffmpeg;
        $video = $ffmpeg->open($inputFiles[0]);
        $format = $this->format;;
        $format->setAudioCodec("libmp3lame");
        $output_path_file = public_path($outputPathfile);
        $video->concat($inputFiles)->saveFromDifferentCodecs($format, $output_path_file);
    }


    /**
     * 获取视频的帧率
     * @param string $video_path 视频路径
     * @param string $new_video_path 输出视频路径
     * @param int $frame_rate 帧速率
     * @param int $gop
     * @return void
     */
    public function getVideoFrameRate(string $video_path,string $new_video_path,int $frame_rate=3000,int $gop=120)
    {
        $ffmpeg = $this->ffmpeg;
        $paths = public_path($video_path);
        $out_path = public_path($new_video_path);
        $video = $ffmpeg->open($paths);
        $format = $this->format;
        $video->filters()->framerate(new FrameRate($frame_rate), $gop);
        $video->save($format, $out_path);
    }

    /**
     * 截取视频/音频
     * @param string $video_path 视频路径
     * @param string $new_video_path 输出视频路径
     * @param int $start 第 $start 秒开始
     * @param $duration --取 $duration 秒，若 $duration 不填，则截取至最后
     * @return void
     */
    public function getCaptureVideoAudio(string $video_path,string $new_video_path,int $start, $duration = null)
    {
        $ffmpeg = $this->ffmpeg;
        $video = $ffmpeg->open($video_path);
        $format = $this->format;
        if($duration){
            $video->filters()->clip(TimeCode::fromSeconds($start),$duration);
        }else{
            $video->filters()->clip(TimeCode::fromSeconds($start));
        }
        $video->save($format, $new_video_path);
    }

    /**
     * 视频裁剪
     * @param string $video_path
     * @param string $new_video_path
     * @param int $width
     * @param int $height
     * @return void
     */
    public function getCropVideo(string $video_path,string $new_video_path,int $width, int $height)
    {
        $ffmpeg = $this->ffmpeg;
        $video = $ffmpeg->open($video_path);
        $format = $this->format;
        //Point($x, $y, $dynamic = false) $x 和 $y 为裁剪的起始坐标，$dynamic 为是否动态裁剪 裁剪出来的视频就是画面从左边动态的移动到右边，然后就固定在左边 100 为速度，值越大，移动速度越快
        //crop(Point $point, Dimension $dimension) $dimension 为裁剪后的尺寸
        $video->filters()->crop(new Point("t*100", 0,true), new Dimension($width, $height));
        $video->save($format, $new_video_path);
    }

    /**
     * 音频转换
     * @param string $video_path
     * @param string $new_video_path
     * @param string $format_type 转换格式
     * @param int $setAudioChannels 声道设置，1单声道，2双声道，3立体声
     * @param int $setAudioKiloBitrate 比特率
     * @return void
     */
    public function getAudioConversion(string $video_path,string $new_video_path,string $format_type="flac",int $setAudioChannels=2 ,int $setAudioKiloBitrate=256)
    {
        $ffmpeg = $this->ffmpeg;
        $paths = public_path($video_path);
        $audio = $ffmpeg->open($paths);
        $out_path = public_path($new_video_path);
        //Flac 为无损压缩格式
        if ($format_type== "mp3"){
            $format = new Mp3();
        }elseif ($format_type== "wav"){
            $format = new Wav();
        }elseif ($format_type== "vorbis"){
            $format = new Vorbis();
        }elseif ($format_type== "libfdk_aac"){
            $format = new Aac();
        }else{
            $format = new Flac();
        }
        $format->on('progress', function ($audio, $format, $percentage) {
            echo "$percentage % 进度";
        });
        $format->setAudioChannels($setAudioChannels)->setAudioKiloBitrate($setAudioKiloBitrate);
        $audio->save($format, $out_path);
    }

    /**
     * 音频添加元数据
     * 目前支持的数据是 title(标题)，artist(艺术家)，album(专辑)，composer(作曲家)，track(轨道)，year(年)，description(描述)，artwork(艺术作品)
     * @param string $video_path 原视频路径
     * @param string $new_video_path
     * @param string $title 标题
     * @param string $artist 艺术家
     * @param string $album 专辑
     * @param string $composer 作曲家
     * @param int $track 轨道
     * @param int $year 年
     * @param string $description 描述
     * @return void
     */
    public function getAddingMetadataAudio(string $video_path,string $new_video_path,string $title,string $artist,string $album,string $composer,int $track,int $year,string $description)
    {
        $ffmpeg = $this->ffmpeg;
        $paths = public_path($video_path);
        $out_path = public_path($new_video_path);
        $audio = $ffmpeg->open($paths);
        $audio->filters()->addMetadata([
            "title" => $title,
            "artist" =>$artist,
            "album" => $album,
            "composer" => $composer,
            "track" => $track,
            "year" => $year,
            "description" => $description,
        ]);
        $format = new Mp3();
        $audio->save($format, $out_path);
    }

    /**
     * 从视频中提取动图
     * @param string $video_path
     * @param string $new_video_path
     * @param int $quantity
     * @param int $width
     * @param int $height
     * @param int $duration
     * @return void
     */
    public function getExtractingDynamicImagesVideos(string $video_path,string $new_video_path,int $quantity=10,int $width=400,int $height=200,int $duration=3)
    {
        $ffmpeg = $this->ffmpeg;
        $paths = public_path($video_path);
        $out_path = public_path($new_video_path);
        $video = $ffmpeg->open($paths);
        // gif(TimeCode $at, Dimension $dimension, $duration = null) 从第 $at 秒开始提取，持续 $duration 秒，保存为 $dimension指定大小（下面的例子为400x200） 的gif 若不设置 $duration ，将会得到一个静止的gif图
        $video->gif(TimeCode::fromSeconds($quantity), new Dimension($width, $height), $duration)
            ->save($out_path);
    }

    /**
     * 使用 FFProbe 提取元数据
     * @param string $video_path
     * @return FFProbe\DataMapping\Format
     */
    public function getExtractMetadata(string $video_path)
    {
        $ffprobe  = $this->ffprobe;
        $paths = public_path($video_path);
        return $ffprobe->format($paths);
    }




}
